home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ddj9304.zip / 1993-APR.ZIP / CPROG.ASC next >
Text File  |  1993-02-17  |  29KB  |  1,029 lines

  1. _C PROGRAMMING COLUMN_
  2. by Al Stevens
  3.  
  4. [LISTING ONE]
  5.  
  6. // ------------- test.cpp
  7. #include "dflatpp.h"
  8.  
  9. extern MenuBarItem aMenu[];
  10.  
  11. // ------- application definition
  12. class myAppl : public Application {
  13. public:
  14.     myAppl() : Application("Hello",aMenu) {}
  15.     // ----- menu command functions
  16.     void DoNew()  {}
  17.     void DoOpen() {}
  18.     void DoExit() { CloseWindow(); }
  19. };
  20. // --------- MenuSelection objects
  21. MenuSelection
  22.     NewCmd  ("~New",  (void (DFWindow::*)()) &myAppl::DoNew ),
  23.     OpenCmd ("~Open", (void (DFWindow::*)()) &myAppl::DoOpen ),
  24.     ExitCmd ("E~xit   Alt+F4", (void (DFWindow::*)()) &myAppl::DoExit, ALT_F4);
  25.  
  26. // --------- File menu definition
  27. MenuSelection *File[] = {
  28.     &NewCmd,
  29.     &OpenCmd,
  30.     &SelectionSeparator,
  31.     &ExitCmd,
  32.     NULL
  33. };
  34. // --------- menu bar definition
  35. MenuBarItem aMenu[] = {
  36.     MenuBarItem( "~File",    File    ),
  37.     MenuBarItem( NULL )
  38. };
  39. void main()
  40. {
  41.     myAppl *aWnd = new myAppl;
  42.     while (desktop.DispatchEvents())
  43.         ;
  44.     delete aWnd;
  45. }
  46.  
  47.  
  48.  
  49. [LISTING TWO]
  50.  
  51. // -------- menubar.h 
  52. #ifndef MENUBAR_H
  53. #define MENUBAR_H
  54.  
  55. #include "textbox.h"
  56. #include "popdown.h"
  57.  
  58. class MenuBarItem    {
  59. public:
  60.     String *title;       // menu bar selection label
  61.     int x1;              // 1st label position on bar
  62.     int x2;              // last  "      "     "   "
  63.     MenuSelection **ms;  // popdown selection list
  64.     PopDown *popdown;    // popdown window
  65.     void (*menuprep)();  // menu prep function
  66.     MenuBarItem(char *Title, MenuSelection **Ms = NULL,
  67.                              void (*MenuPrep)() = NULL);
  68.     ~MenuBarItem() { if (title) delete title; }
  69. };
  70. class MenuBar : public TextBox    {
  71.     MenuBarItem *menuitems; // list of popdowns
  72.     int menucount;          // count of popdowns
  73.     int selection;          // current selection on the bar
  74.     Bool ispoppeddown;      // True = a menu is down
  75.     DFWindow *oldfocus;     // previous focus
  76.     void SetColors();
  77.     void Select();
  78.     Bool AcceleratorKey(int key);
  79.     Bool ShortCutKey(int key);
  80. public:
  81.     MenuBar(MenuBarItem *MenuItems, DFWindow *par);
  82.     ~MenuBar();
  83.     // -------- menubar API messages
  84.     void Keyboard(int key);
  85.     void LeftButton(int mx, int my);
  86.     Bool SetFocus();
  87.     void ResetFocus();
  88.     void Paint();
  89.     void Select(int sel);
  90.     void SetSelection(int sel);
  91.     void ParentSized(int xdif, int ydif);
  92. };
  93. #endif
  94.  
  95.  
  96.  
  97. [LISTING THREE]
  98.  
  99. // ------------- menubar.cpp
  100. #include <ctype.h>
  101. #include "desktop.h"
  102. #include "menubar.h"
  103. #include "menusel.h"
  104.  
  105. // -------- construct a menubar item
  106. MenuBarItem::MenuBarItem(char *Title, MenuSelection **Ms,void (*MenuPrep)())
  107. {
  108.     if (Title != NULL)
  109.         title = new String(Title);
  110.     else
  111.         title = NULL;
  112.     ms = Ms;
  113.     menuprep = MenuPrep;
  114.     popdown = NULL;
  115. }
  116. // -------- construct a menubar
  117. MenuBar::MenuBar( MenuBarItem *MenuItems, DFWindow *par) :
  118.             TextBox( par->ClientLeft(), par->ClientTop()-1,
  119.                         1, par->ClientWidth(), par)
  120. {
  121.     windowtype = MenubarWindow;
  122.     menuitems = MenuItems;
  123.     SetAttribute(NOCLIP);
  124.     SetColors();
  125.     selection = -1;
  126.     ispoppeddown = False;
  127.     MenuBarItem *menu = menuitems;
  128.     menucount = 0;
  129.     oldfocus = NULL;
  130.     int off = 2;
  131.     SetTextLength(desktop.screen().Width()*2);
  132.     while (menu->title != NULL)    {
  133.         int len = menu->title->Strlen()-1;
  134.         menu->x1 = off;
  135.         menu->x2 = off+len-1;
  136.         off += len+2;
  137.         String ttl("  ");
  138.         ttl += *(menu->title);
  139.         AddText(ttl);
  140.         int n = text->Strlen()-1;
  141.         (*text)[n] = '\0';
  142.         menu->popdown = new PopDown(this, menu->ms);
  143.         menu++;
  144.         menucount++;
  145.     }
  146. }
  147. // -------- menubar destructor
  148. MenuBar::~MenuBar()
  149. {
  150.     MenuBarItem *menu = menuitems;
  151.     while (menu->title != NULL)    {
  152.         delete menu->popdown;
  153.         menu++;
  154.     }
  155.     TextBox::CloseWindow();
  156. }
  157. // -------- set the fg/bg colors for the window 
  158. void MenuBar::SetColors()
  159. {
  160.     colors.fg = BLACK;
  161.     colors.bg = LIGHTGRAY;
  162.     colors.sfg = BLACK;
  163.     colors.sbg = CYAN;
  164.     colors.ffg = BLACK;
  165.     colors.fbg = LIGHTGRAY;
  166.     colors.hfg = BLACK;
  167.     colors.hbg = LIGHTGRAY;
  168.     shortcutfg = RED;
  169. }
  170. // ---- menubar gets the focus
  171. Bool MenuBar::SetFocus()
  172. {
  173.     if (oldfocus == NULL)
  174.         if (desktop.InFocus() != NULL)
  175.             if (desktop.InFocus()->State() != ISCLOSING)
  176.                 oldfocus = desktop.InFocus();
  177.     return TextBox::SetFocus();
  178. }
  179. // ---- menubar loses the focus
  180. void MenuBar::ResetFocus()
  181. {
  182.     if (!ispoppeddown)    {
  183.         SetSelection(-1);
  184.         oldfocus = NULL;
  185.     }
  186.     TextBox::ResetFocus();
  187. }
  188. // -------- paint the menubar
  189. void MenuBar::Paint()
  190. {
  191.     WriteShortcutLine(0, colors.fg, colors.bg);
  192.     if (selection != -1)    {
  193.         int x = menuitems[selection].x1;
  194.         int len = menuitems[selection].x2-x+2;
  195.         String sel = text->mid(len, x+selection);
  196.         DisplayShortcutField(sel,x,0,colors.sfg,colors.sbg);
  197.     }
  198. }
  199. // ------- left mouse button is pressed
  200. void MenuBar::LeftButton(int mx, int)
  201. {
  202.     mx -= Left();
  203.     MenuBarItem *menu = menuitems;
  204.     int sel = 0;
  205.     while (menu->title != NULL)    {
  206.         if (mx >= menu->x1 && mx <= menu->x2)    {
  207.             if (selection != sel || !ispoppeddown)    {
  208.                 if (ispoppeddown)    {
  209.                     PopDown *pd = menuitems[selection].popdown;
  210.                     if (pd->isOpen())
  211.                         pd->CloseMenu(False);
  212.                 }
  213.                 Select(sel);
  214.             }
  215.             return;
  216.         }
  217.         sel++;
  218.         menu++;
  219.     }
  220.     if (selection == -1)
  221.         SetSelection(0);
  222. }
  223. void MenuBar::SetSelection(int sel)
  224. {
  225.     selection = sel;
  226.     Paint();
  227. }
  228. // ----- programmed selection
  229. void MenuBar::Select(int sel)    {
  230.     selection = sel;
  231.     Select();
  232. }
  233. // ------- user selection
  234. void MenuBar::Select()
  235. {
  236.     Paint();
  237.     ispoppeddown = True;
  238.     MenuBarItem &mb = *(menuitems+selection);
  239.     int lf = Left() + mb.x1;
  240.     int tp = Top()+1;
  241.     if (mb.menuprep != NULL)
  242.         (*mb.menuprep)();
  243.     mb.popdown->OpenMenu(lf, tp);
  244. }
  245. // ------ test for popdown accelerator key
  246. Bool MenuBar::AcceleratorKey(int key)
  247. {
  248.     MenuBarItem *menu = menuitems;
  249.     while (menu->title != NULL)    {
  250.         PopDown *pd = menu->popdown;
  251.         if (pd->AcceleratorKey(key))
  252.             return True;
  253.         menu++;
  254.     }
  255.     return False;
  256. }
  257. // ------ test for menubar shortcut key
  258. Bool MenuBar::ShortCutKey(int key)
  259. {
  260.     int altkey = desktop.keyboard().AltConvert(key);
  261.     MenuBarItem *menu = menuitems;
  262.     int sel = 0;
  263.     while (menu->title != NULL)    {
  264.         int off = menu->title->FindChar(SHORTCUTCHAR);
  265.         if (off != -1)    {
  266.             String &cp = *(menu->title);
  267.             int c = cp[off+1];
  268.             if (tolower(c) == altkey)    {
  269.                 SetFocus();
  270.                 Select(sel);
  271.                 return True;
  272.             }
  273.         }
  274.         sel++;
  275.         menu++;
  276.     }
  277.     return False;
  278. }
  279. // -------- keystroke while menubar has the focus
  280. void MenuBar::Keyboard(int key)
  281. {
  282.     if (AcceleratorKey(key))
  283.         return;
  284.     if (!ispoppeddown && ShortCutKey(key))
  285.         return;
  286.     switch (key)    {
  287.         case F10:
  288.             if (ispoppeddown)
  289.                 break;
  290.             if (this != desktop.InFocus())    {
  291.                 if (selection == -1)
  292.                     selection = 0;
  293.                 SetFocus();
  294.                 break;
  295.             }
  296.             // ------ fall through
  297.         case ESC:
  298.             ispoppeddown = False;
  299.             SetSelection(-1);
  300.             if (oldfocus != NULL)
  301.                 oldfocus->SetFocus();
  302.             else
  303.                 parent->SetFocus();
  304.             break;
  305.         case FWD:
  306.             selection++;
  307.             if (selection == menucount)
  308.                 selection = 0;
  309.             if (ispoppeddown)
  310.                 Select();
  311.             else
  312.                 Paint();
  313.             break;
  314.         case BS:
  315.             if (selection == 0)
  316.                 selection = menucount;
  317.             --selection;
  318.             if (ispoppeddown)
  319.                 Select();
  320.             else
  321.                 Paint();
  322.             break;
  323.         case '\r':
  324.             if (selection != -1)
  325.                 Select();
  326.             break;
  327.         case ALT_F6:
  328.             TextBox::Keyboard(key);
  329.             break;
  330.         default:
  331.             break;
  332.     }
  333. }
  334. // ---- resize the menubar when the application window resizes
  335. void MenuBar::ParentSized(int xdif, int)
  336. {
  337.     Size(Right()+xdif, Bottom());
  338. }
  339.  
  340.  
  341.  
  342. [LISTING FOUR]
  343.  
  344. // ------- menusel.h
  345. #ifndef MENUSEL_H
  346. #define MENUSEL_H
  347.  
  348. #include <stdio.h>
  349. #include "strings.h"
  350. #include "dfwindow.h"
  351.  
  352. enum MenuType {
  353.     NORMAL,
  354.     TOGGLE,
  355.     CASCADER,
  356.     SEPARATOR
  357. };
  358. enum Toggle { Off, On };
  359.  
  360. class DFWindow;
  361. class PopDown;
  362.  
  363. #define NULLFUNC (void (DFWindow::*)())NULL
  364. class MenuSelection    {
  365.     String *label;          // selection label
  366.     void (DFWindow::*cmdfunction)(); // selection function
  367.     MenuType type;          // NORMAL, TOGGLE,
  368.                             // CASCADER, SEPARATOR
  369.     Bool isenabled;         // True = enabled, False = disabled
  370.     int accelerator;        // accelerator key
  371.     PopDown *cascade;       // cascaded menu window
  372.     MenuSelection **cascaders; // cascaded menu selection list
  373.     Toggle toggle;          // On or Off
  374.     void NullSelection();   // Build a null selection
  375.     friend PopDown;
  376.     void CommonConstructor(    char *Label,
  377.                             int Accelerator,
  378.                             void (DFWindow::*CmdFunction)(),
  379.                             Bool Active,
  380.                             MenuType Type,
  381.                             Toggle Tgl,
  382.                             MenuSelection **Cascaders = NULL);
  383. public:
  384.     MenuSelection(    char *Label, 
  385.                     void (DFWindow::*CmdFunction)() = 0,
  386.                     int Accelerator=0,
  387.                     Bool Active=True );
  388.     MenuSelection(    char *Label,
  389.                     void (DFWindow::*CmdFunction)(),
  390.                     Toggle Tgl,
  391.                     int Accelerator=0,
  392.                     Bool Active=True );
  393.     MenuSelection(    char *Label,
  394.                     MenuSelection **Cascaders,
  395.                     int Accelerator=0,
  396.                     Bool Active=True );
  397.     MenuSelection(MenuType Type);
  398.  
  399.     Bool isEnabled()    { return isenabled; }
  400.     void Enable()       { isenabled = True; }
  401.     void Disable()      { isenabled = False; }
  402.     void SetToggle()    { toggle = On; }
  403.     void ClearToggle()  { toggle = Off; }
  404.     void InvertToggle() { toggle = toggle == On ? Off : On; }
  405.     Bool isToggled()    { return (Bool) (toggle == On); };
  406.     Type()              { return type; }
  407. };
  408. extern MenuSelection SelectionSeparator;
  409. extern MenuSelection SelectionTerminator;
  410.  
  411. #endif
  412.  
  413.  
  414.  
  415.  
  416. [LISTING FIVE]
  417.  
  418. // ------- menusel.cpp
  419. #include <string.h>
  420. #include "menusel.h"
  421.  
  422. MenuSelection SelectionSeparator(SEPARATOR);
  423. void MenuSelection::NullSelection()
  424. {
  425.     label = NULL;
  426.     cmdfunction = NULL;
  427.     type = NORMAL;
  428.     cascade = NULL;
  429.     isenabled = True;
  430.     accelerator = 0;
  431.     cascade = NULL;
  432.     toggle = Off;
  433. }
  434. void MenuSelection::CommonConstructor(
  435.                                char *Label,
  436.                                int Accelerator,
  437.                                void (DFWindow::*CmdFunction)(),
  438.                                Bool Active,
  439.                                MenuType Type,
  440.                                Toggle Tgl,
  441.                                MenuSelection **Cascaders)
  442. {
  443.     NullSelection();
  444.     if (Label != NULL)
  445.         label = new String(Label);
  446.     accelerator = Accelerator;
  447.     cmdfunction = CmdFunction;
  448.     isenabled = Active;
  449.     type = Type;
  450.     toggle = Tgl;
  451.     cascaders = Cascaders;
  452. }
  453. MenuSelection::MenuSelection(  char *Label,
  454.                                void (DFWindow::*CmdFunction)(),
  455.                                int Accelerator, Bool Active )
  456. {
  457.     CommonConstructor(Label, Accelerator, CmdFunction, Active, NORMAL, Off);
  458. }
  459. MenuSelection::MenuSelection(  char *Label,
  460.                                void (DFWindow::*CmdFunction)(),
  461.                                Toggle Tgl, int Accelerator, Bool Active)
  462. {
  463.     CommonConstructor(Label, Accelerator, CmdFunction, Active, TOGGLE, Tgl);
  464. }
  465.  
  466. MenuSelection::MenuSelection(char *Label,
  467.                             MenuSelection **Cascaders,
  468.                             int Accelerator, Bool Active )
  469. {
  470.     CommonConstructor(Label, Accelerator, NULL, 
  471.                              Active, CASCADER, Off, Cascaders);
  472. }
  473. MenuSelection::MenuSelection(MenuType Type)
  474. {
  475.     NullSelection();
  476.     type = Type;
  477. }
  478.  
  479.  
  480.  
  481. [LISTING SIX]
  482.  
  483. // -------- popdown.h 
  484. #ifndef POPDOWN_H
  485. #define POPDOWN_H
  486.  
  487. #include "desktop.h"
  488. #include "listbox.h"
  489.  
  490. const unsigned char LEDGE          = '\xc3';
  491. const unsigned char REDGE          = '\xb4';
  492. const unsigned char CASCADEPOINTER = '\x10';
  493.  
  494. inline unsigned char CheckMark()
  495. {
  496.     return desktop.screen().Height() == 25 ? 251 : 4;
  497. }
  498. class MenuSelection;
  499. class MenuBar;
  500.  
  501. class PopDown : public ListBox    {
  502.     MenuSelection **selections; // array of selections
  503.     Bool isopen;                // True = menu is open
  504.     Bool iscascaded;            // True = menu is cascaded
  505.     int menuwidth;              // width of menu
  506.     int menuheight;             // height of menu
  507.  
  508.     void BuildMenuLine(int sel);
  509.     void MenuDimensions();
  510.     void SetColors();
  511.     void DisplayMenuLine(int lno);
  512.     Bool ShortCutKey(int key);
  513. protected:
  514.     void ClearSelection();
  515. public:
  516.     PopDown(DFWindow *par, MenuSelection **Selections = NULL)
  517.                         : ListBox(5, 5, par)
  518.             { selections = Selections; OpenWindow(); }
  519.     virtual ~PopDown()
  520.         { if (windowstate != CLOSED) CloseWindow(); }
  521.     // -------- listbox API messages
  522.     void OpenWindow();
  523.     void CloseWindow();
  524.     void OpenMenu(int left, int top);
  525.     void CloseMenu(Bool SendESC = False);
  526.     void Show();
  527.     void Paint();
  528.     void Border();
  529.     void Keyboard(int key);
  530.     void ShiftChanged(int sk);
  531.     void ButtonReleased(int mx, int my);
  532.     void LeftButton(int mx, int my);
  533.     void DoubleClick(int mx, int my);
  534.     void Choose();
  535.     void SetSelection(int sel);
  536.     Bool isOpen() { return isopen; }
  537.     Bool &isCascaded() { return iscascaded; }
  538.     Bool AcceleratorKey(int key);
  539.     Bool ParentisMenu(DFWindow &wnd);
  540.     Bool ParentisMenu() { return ParentisMenu(*this); }
  541. };
  542. #endif
  543.  
  544.  
  545.  
  546. [LISTING SEVEN]
  547.  
  548. // ------------- popdown.cpp
  549. #include <ctype.h>
  550. #include "desktop.h"
  551. #include "popdown.h"
  552. #include "menusel.h"
  553.  
  554. // --------- create a popdown menu
  555. void PopDown::OpenWindow()
  556. {
  557.     windowtype = PopdownWindow;
  558.     if (windowstate == CLOSED)
  559.         ListBox::OpenWindow();
  560.     SetAttribute(BORDER | SHADOW | SAVESELF | NOCLIP);
  561.     selection = 0;
  562.     DblBorder = False;
  563.     isopen = False;
  564.     SetColors();
  565.     iscascaded = False;
  566.     if (selections != NULL)    {
  567.         MenuDimensions();
  568.         SetTextLength(menuwidth * menuheight);
  569.         for (int i = 0; i < menuheight; i++)    {
  570.             MenuSelection &ms = **(selections+i);
  571.             BuildMenuLine(i);
  572.             if (ms.type == CASCADER)    {
  573.                 ms.cascade = new PopDown(this, ms.cascaders);
  574.                 ms.cascade->isCascaded() = True;
  575.             }
  576.         }
  577.         rect.Right() = rect.Left() + menuwidth;
  578.         rect.Bottom() = rect.Top() + menuheight + 1;
  579.     }
  580. }
  581. // ---- shut down a popdown menu
  582. void PopDown::CloseWindow()
  583. {
  584.     if (selections != NULL)    {
  585.         // --- delete all cascader popdowns
  586.         for (int i = 0; selections[i]; i++)    {
  587.             MenuSelection &ms = *selections[i];
  588.             if (ms.type == CASCADER && ms.cascade != NULL)
  589.                 delete ms.cascade;
  590.         }
  591.     }
  592.     ListBox::CloseWindow();
  593. }
  594.  
  595. // ------- pop down the menu
  596. void PopDown::OpenMenu(int left, int top)
  597. {
  598.     Rect rc(0, 0, desktop.screen().Width()-1,
  599.         desktop.screen().Height()-1);
  600.     DFWindow *Wnd = parent;
  601.     while (Wnd != NULL && Wnd->WindowType() == PopdownWindow)
  602.         Wnd = Wnd->Parent();
  603.     if (Wnd != NULL && (Wnd = Wnd->Parent()) != NULL)    {
  604.         Rect rc = Wnd->ClientRect();
  605.         left = min(max(left, rc.Left()), rc.Right() - ClientWidth());
  606.         top = min(max(top, rc.Top()), rc.Bottom() - ClientHeight());
  607.     }
  608.     left = min(max(left, rc.Left()), rc.Right()-ClientWidth()-1);
  609.     top = min(max(top, rc.Top()), rc.Bottom()-ClientHeight()-1);
  610.     isopen = True;
  611.     Move(left, top);
  612.     CaptureFocus();
  613.     Paint();        // in case a command attribute changed
  614. }
  615. // ---------- deactivate the popdown menu
  616. void PopDown::CloseMenu(Bool SendESC)
  617. {
  618.     if (isopen)    {
  619.         // ------- close any open cascaded menus
  620.         PopDown *Wnd = (PopDown *)first;
  621.         while (Wnd != NULL)    {
  622.             Wnd->CloseMenu();
  623.             Wnd = (PopDown *) (Wnd->next);
  624.         }
  625.         Hide();
  626.         isopen = False;
  627.         ReleaseFocus();
  628.         if (parent && !iscascaded && SendESC)
  629.             parent->Keyboard(ESC);
  630.     }
  631. }
  632. void PopDown::Show()
  633. {
  634.     if (isopen)
  635.         ListBox::Show();
  636. }
  637. // -------- build a menu line
  638. void PopDown::BuildMenuLine(int sel)
  639. {
  640.     int wd = menuwidth;
  641.     String ln;
  642.     if (selections[sel]->type == SEPARATOR)
  643.         ln = String(--wd, LINE);
  644.     else    {
  645.         ln = String(" ");
  646.         ln += *(selections[sel]->label);
  647.         int r = wd-ln.Strlen();
  648.         ln += String(r, ' ');
  649.         if (selections[sel]->type == CASCADER)
  650.             ln[wd-1] = CASCADEPOINTER;
  651.     }
  652.     AddText(ln);
  653. }
  654. // -------- compute menu width
  655. void PopDown::MenuDimensions()
  656. {
  657.     int txlen = 0;
  658.     for (int i = 0; selections[i] != NULL; i++)  {
  659.         if (selections[i]->type != SEPARATOR)    {
  660.             int lblen = (selections[i]->label)->Strlen()-1;
  661.             txlen = max(txlen, lblen);
  662.         }
  663.     }
  664.     menuwidth = txlen+4;
  665.     menuheight = i;
  666. }
  667. // -------- set the fg/bg colors for the window 
  668. void PopDown::SetColors()
  669. {
  670.     colors.fg = BLACK;
  671.     colors.bg = CYAN;
  672.     colors.sfg = BLACK;
  673.     colors.sbg = LIGHTGRAY;
  674.     colors.ffg = BLACK;
  675.     colors.fbg = CYAN;
  676.     colors.hfg = DARKGRAY;    // Inactive FG
  677.     colors.hbg = CYAN;        // Inactive FG
  678.     shortcutfg = RED;
  679. }
  680. // ------ display a menu line
  681. void PopDown::DisplayMenuLine(int lno)
  682. {
  683.     if (isopen)    {
  684.         int fg, bg;
  685.         int isActive = selections[lno]->isEnabled();
  686.         int sfg = shortcutfg;
  687.         if (lno == selection)    {
  688.             fg = colors.sfg;
  689.             bg = colors.sbg;
  690.         }
  691.         else if (isActive)    {
  692.             fg = colors.fg;
  693.             bg = colors.bg;
  694.         }
  695.         else     {
  696.             fg = colors.hfg;
  697.             bg = colors.hbg;
  698.         }
  699.         if (!isActive)
  700.             shortcutfg = fg;
  701.         WriteShortcutLine(lno, fg, bg);
  702.         shortcutfg = sfg;
  703.     }
  704. }
  705. // ------ set no selection current
  706. void PopDown::ClearSelection()
  707. {
  708.     if (selection != -1)    {
  709.         int sel = selection;
  710.         selection = -1;
  711.         DisplayMenuLine(sel);
  712.     }
  713. }
  714. // ------ set a current menu selection
  715. void PopDown::SetSelection(int sel)
  716. {
  717.     ClearSelection();
  718.     if (sel >= 0 && sel < wlines)    {
  719.         selection = sel;
  720.         DisplayMenuLine(sel);
  721.     }
  722. }
  723. // ---------- paint the menu
  724. void PopDown::Paint()
  725. {
  726.     if (text == NULL)
  727.         ListBox::Paint();
  728.     else    {
  729.         for (int i = 0; i < wlines; i++)    {
  730.             if (selections[i]->type == TOGGLE)    {
  731.                 char *cp = TextLine(i);
  732.                 if (selections[i]->toggle == On)
  733.                     *cp = CheckMark();
  734.                 else
  735.                     *cp = ' ';
  736.             }
  737.             DisplayMenuLine(i);
  738.         }
  739.     }
  740. }
  741. // --------- paint the menu's border
  742. void PopDown::Border()
  743. {
  744.     if (isopen && isVisible())    {
  745.         int fg = colors.ffg;
  746.         int bg = colors.fbg;
  747.         int rt = Width()-1;
  748.         ListBox::Border();
  749.         for (int i = 0; i < wlines; i++)    {
  750.             if (selections[i]->type == SEPARATOR)    {
  751.                 WriteWindowChar(LEDGE, 0, i+1, fg, bg);
  752.                 WriteWindowChar(REDGE, rt, i+1, fg, bg);
  753.             }
  754.         }
  755.     }
  756. }
  757. // ------- test for a menu selection accelerator key
  758. Bool PopDown::AcceleratorKey(int key)
  759. {
  760.     for (int i = 0; i < wlines; i++)    {
  761.         MenuSelection &ms = **(selections+i);
  762.         if (key == ms.accelerator)    {
  763.             SetSelection(i);
  764.             Choose();
  765.             return True;
  766.         }
  767.     }
  768.     return False;
  769. }
  770. // ------- test for a menu selection shortcut key
  771. Bool PopDown::ShortCutKey(int key)
  772. {
  773.     key = tolower(key);
  774.     for (int i = 0; i < wlines; i++)    {
  775.         MenuSelection &ms = **(selections+i);
  776.         int off = ms.label->FindChar(SHORTCUTCHAR);
  777.         if (off != -1)    {
  778.             String &cp = *ms.label;
  779.             int c = cp[off+1];
  780.             if (key == tolower(c))    {
  781.                 SetSelection(i);
  782.                 Choose();
  783.                 return True;
  784.             }
  785.         }
  786.     }
  787.     return False;
  788. }
  789. // ----- keystroke while menu is popped down
  790. void PopDown::Keyboard(int key)
  791. {
  792.     if (AcceleratorKey(key))
  793.         return;
  794.     if (ShortCutKey(key))
  795.         return;
  796.     switch (key)    {
  797.         case UP:
  798.             if (selection == 0)    {
  799.                 SetSelection(wlines-1);
  800.                 return;
  801.             }
  802.             if (selections[selection-1]->type == SEPARATOR)  {
  803.                 SetSelection(selection-2);
  804.                 return;
  805.             }
  806.             break;
  807.         case DN:
  808.             if (selection == wlines-1)    {
  809.                 SetSelection(0);
  810.                 return;
  811.             }
  812.             if (selections[selection+1]->type == SEPARATOR)  {
  813.                 SetSelection(selection+2);
  814.                 return;
  815.             }
  816.             break;
  817.         case ESC:
  818.             CloseMenu(ParentisMenu());
  819.             return;
  820.         case FWD:
  821.         case BS:
  822.             CloseMenu();
  823.             if (parent != NULL)    {
  824.                 parent->Keyboard(key);
  825.                 return;
  826.             }
  827.             break;
  828.         default:
  829.             break;
  830.     }
  831.     ListBox::Keyboard(key);
  832. }
  833. // ----- shift key status changed
  834. void PopDown::ShiftChanged(int sk)
  835. {
  836.     if (sk & ALTKEY)
  837.         CloseMenu(ParentisMenu());
  838. }
  839. // ---------- Left mouse button was clicked
  840. void PopDown::LeftButton(int mx, int my)
  841. {
  842.     if (ClientRect().Inside(mx, my))    {
  843.         if (my != prevmouseline)    {
  844.             int y = my - ClientTop();
  845.             if (selections[y]->type != SEPARATOR)
  846.                 SetSelection(y);
  847.         }
  848.     }
  849.     else if (!rect.Inside(mx, my))    {
  850.         if (parent && my == parent->Bottom())
  851.             parent->LeftButton(mx, my);
  852.     }
  853.     prevmouseline = my;
  854.     prevmousecol = mx;
  855. }
  856. // ---------- Left mouse button was double-clicked
  857. void PopDown::DoubleClick(int mx, int my)
  858. {
  859.     if (!rect.Inside(mx, my))    {
  860.         CloseMenu();
  861.         if (parent)
  862.             parent->DoubleClick(mx, my);
  863.     }
  864. }
  865. // ---------- Left mouse button was released
  866. void PopDown::ButtonReleased(int mx, int my)
  867. {
  868.     if (ClientRect().Inside(mx, my))    {
  869.         if (prevmouseline == my && prevmousecol == mx)
  870.             if (selections[my-ClientTop()]->type != SEPARATOR)
  871.                 Choose();
  872.     }
  873.     else if (!rect.Inside(mx, my))    {
  874.         DFWindow *Wnd = desktop.inWindow(mx, my);
  875.         if (!(Wnd == parent && my == Top()-1 &&
  876.                 mx >= Left() && mx <= Right()))    {
  877.             CloseMenu(ParentisMenu());
  878.             if (Wnd != NULL && Wnd != desktop.InFocus())
  879.                 Wnd->SetFocus();
  880.         }
  881.     }
  882. }
  883. // --------- user chose a menu selection
  884. void PopDown::Choose()
  885. {
  886.     MenuSelection &ms = *selections[selection];
  887.     if (ms.isEnabled())    {
  888.         if (ms.type == CASCADER && ms.cascade != NULL)
  889.             // -------- cascaded menu
  890.             ms.cascade->OpenMenu(Right(), Top()+selection);
  891.         else    {
  892.             if (ms.type == TOGGLE)    {
  893.                 // ---- toggle selection
  894.                 ms.InvertToggle();
  895.                 char *cp = TextLine(selection);
  896.                 if (*cp == CheckMark())
  897.                     *cp = ' ';
  898.                 else
  899.                     *cp = CheckMark();
  900.                 DisplayMenuLine(selection);
  901.             }
  902.             if (ms.cmdfunction != NULL)    {
  903.                 // ---- there is a function associated
  904.                 DFWindow *wnd = (DFWindow *)this;
  905.                 // --- close all menus
  906.                 while (wnd &&
  907.                         wnd->WindowType() == PopdownWindow) {
  908.                     ((PopDown *)wnd)->CloseMenu();
  909.                     wnd = wnd->Parent();
  910.                 }
  911.                 if (wnd && wnd->WindowType() == MenubarWindow){
  912.                     wnd->Keyboard(ESC);
  913.                     wnd = wnd->Parent();
  914.                 }
  915.                 if (wnd)
  916.                     // ---- execute the function
  917.                     (wnd->*ms.cmdfunction)();
  918.             }
  919.         }
  920.     }
  921.     else
  922.         desktop.speaker().Beep();    // disabled selection
  923. }
  924. inline Bool isMenu(DFWindow *wnd)
  925. {
  926.     if (wnd != NULL)    {
  927.         WndType wt = wnd->WindowType();
  928.         return (Bool) (wt==MenubarWindow || wt==PopdownWindow);
  929.     }
  930.     return False;
  931. }
  932. // ----- test for the parent as menu or menubar
  933. Bool PopDown::ParentisMenu(DFWindow &wnd)
  934. {
  935.     return isMenu(wnd.Parent());
  936. }
  937.  
  938.  
  939.  
  940. [LISTING EIGHT]
  941.  
  942. // --------------- ctlmenu.cpp
  943. #include "dflatpp.h"
  944. #include "frame.h"
  945.  
  946. MenuSelection RestoreCmd  ("~Restore",  &DFWindow::Restore);
  947. MenuSelection MoveCmd     ("~Move",     &DFWindow::CtlMenuMove);
  948. MenuSelection SizeCmd     ("~Size",     &DFWindow::CtlMenuSize);
  949. MenuSelection MinimizeCmd ("Mi~nimize", &DFWindow::Minimize);
  950. MenuSelection MaximizeCmd ("Ma~ximize", &DFWindow::Maximize);
  951. MenuSelection CloseDocCmd ("~Close [Ctrl+F4]",&DFWindow::CloseWindow, CTRL_F4);
  952. MenuSelection CloseApCmd  ("~Close [Alt+F4]",&DFWindow::CloseWindow, ALT_F4);
  953.  
  954. MenuSelection *ControlMenu[8];
  955.  
  956. MenuBarItem CtlMenu[] = {
  957.     MenuBarItem( "", ControlMenu ),
  958.     MenuBarItem( NULL, NULL )
  959. };
  960. void DFWindow::OpenCtlMenu()
  961. {
  962.     if (ctlmenu != NULL)
  963.         delete ctlmenu;
  964.     int mn = 0;
  965.     if (attrib & (MINBOX | MAXBOX))
  966.         ControlMenu[mn++] = &RestoreCmd;
  967.     if (attrib & MOVEABLE)
  968.         ControlMenu[mn++] = &MoveCmd;
  969.     if (attrib & SIZEABLE)
  970.         ControlMenu[mn++] = &SizeCmd;
  971.     if (attrib & MINBOX)
  972.         ControlMenu[mn++] = &MinimizeCmd;
  973.     if (attrib & MAXBOX)
  974.         ControlMenu[mn++] = &MaximizeCmd;
  975.     if (mn != 0)
  976.         ControlMenu[mn++] = &SelectionSeparator;
  977.     if (Parent())
  978.         ControlMenu[mn++] = &CloseDocCmd;
  979.     else
  980.         ControlMenu[mn++] = &CloseApCmd;
  981.     ControlMenu[mn] = NULL;
  982.  
  983.     MinimizeCmd.Disable();
  984.     MaximizeCmd.Disable();
  985.     RestoreCmd.Disable();
  986.     SizeCmd.Disable();
  987.     MoveCmd.Disable();
  988.  
  989.     switch (windowstate)    {
  990.         case ISRESTORED:
  991.             if (attrib & MINBOX)
  992.                 MinimizeCmd.Enable();
  993.             if (attrib & MAXBOX)
  994.                 MaximizeCmd.Enable();
  995.             MoveCmd.Enable();
  996.             SizeCmd.Enable();
  997.             break;
  998.         case ISMINIMIZED:
  999.             RestoreCmd.Enable();
  1000.             MoveCmd.Enable();
  1001.             break;
  1002.         case ISMAXIMIZED:
  1003.             RestoreCmd.Enable();
  1004.             if (attrib & MINBOX)
  1005.                 MinimizeCmd.Enable();
  1006.             break;
  1007.     }
  1008.     ctlmenu = new PopDown(this, ControlMenu);
  1009.     ctlmenu->OpenMenu(Left()+1, Top()+1);
  1010. }
  1011. void DFWindow::DeleteCtlMenu()
  1012. {
  1013.     if (ctlmenu != NULL)
  1014.         delete ctlmenu;
  1015.     ctlmenu = NULL;
  1016. }
  1017. void DFWindow::CtlMenuMove()
  1018. {
  1019.     desktop.mouse().SetPosition(Left(), Top());
  1020.     new Frame(this, Left());
  1021. }
  1022. void DFWindow::CtlMenuSize()
  1023. {
  1024.     desktop.mouse().SetPosition(Right(), Bottom());
  1025.     new Frame(this);
  1026. }
  1027.  
  1028.  
  1029.